/* Copyright ©, 2022, ECARX (Hubei) Technology Co., Ltd. All rights reserved.
 *
 * NOTICE: This file contains information which is confidential and proprietary to ECARX
 * (Hubei) Technology Co., Ltd. and/or other developers. No license to any intellectual
 * property rights contained within this file is either granted or implied except as may be
 * provided in an agreement with ECARX (Hubei) Technology Co., Ltd.
 *
 * Any unauthorized copying or distribution of content from this file in whole or in part
 * is prohibited.
 */
#define LOG_NDEBUG 0
#define LOG_TAG "PropValueStore"
#include "PropValueStore.h"

#include "VHalLog.h"
#include <utils/SystemClock.h>
namespace ecarx::vhal::core::property::storage {

static bool isPropertyChanged(const VehiclePropValue& current, const VehiclePropValue& last) {
    bool changed = false;
    if (current.status != last.status) {
        changed = true;
    } else {
        VehiclePropertyType type = getPropType(current.prop);
        if (type == VehiclePropertyType::BOOLEAN || type == VehiclePropertyType::INT32 ||
            type == VehiclePropertyType::INT32_VEC) {
            changed = current.value.int32Values != last.value.int32Values;
        } else if (type == VehiclePropertyType::INT64 || type == VehiclePropertyType::INT64_VEC) {
            changed = current.value.int64Values != last.value.int64Values;
        } else if (type == VehiclePropertyType::FLOAT || type == VehiclePropertyType::FLOAT_VEC) {
            changed = current.value.floatValues != last.value.floatValues;
        } else if (type == VehiclePropertyType::STRING) {
            changed = current.value.stringValue != last.value.stringValue;
        } else if (type == VehiclePropertyType::BYTES) {
            changed = current.value.bytes != last.value.bytes;
        } else if (type == VehiclePropertyType::MIXED) {
            changed = current.value.int32Values != last.value.int32Values ||
                      current.value.int64Values != last.value.int64Values ||
                      current.value.floatValues != last.value.floatValues ||
                      current.value.stringValue != last.value.stringValue ||
                      current.value.bytes != last.value.bytes;
        }
    }

    return changed;
}

bool PropValueStore::writeValue(const VehiclePropValue& propValue, CommitMode mode) {
    MuxGuard g(mLock);
    RecordId recId = getRecordIdLocked(propValue);
    VehiclePropValue* valueToUpdate = const_cast<VehiclePropValue*>(getValueOrNullLocked(recId));
    if (valueToUpdate == nullptr) {
        mPropertyValues.insert({recId, propValue});
        mReportValues.insert_or_assign(recId, CollectValue{.firstValue =
                                                               {
                                                                   .areaId = propValue.areaId,
                                                                   .prop = propValue.prop,
                                                               },
                                                           .lastValue = propValue,
                                                           .mode = mode});
        return true;
    }

    bool changed = isPropertyChanged(propValue, *valueToUpdate);
    auto iter = mReportValues.try_emplace(
        recId, CollectValue{.firstValue = *valueToUpdate, .lastValue = propValue, .mode=mode});
    if (!iter.second) {
        iter.first->second.lastValue = propValue;
    }
    if(changed){
        valueToUpdate->timestamp = propValue.timestamp;
        valueToUpdate->value = propValue.value;
        valueToUpdate->status = propValue.status;
    }
    return changed;
}

void PropValueStore::removeValue(const VehiclePropValue& propValue) {
    MuxGuard g(mLock);
    RecordId recId = getRecordIdLocked(propValue);
    auto it = mPropertyValues.find(recId);
    if (it != mPropertyValues.end()) {
        mPropertyValues.erase(it);
    }
}

std::vector<VehiclePropValue> PropValueStore::readAllValues() const {
    MuxGuard g(mLock);
    std::vector<VehiclePropValue> allValues;
    allValues.reserve(mPropertyValues.size());
    for (auto&& it : mPropertyValues) {
        allValues.push_back(it.second);
    }
    return allValues;
}

std::unique_ptr<VehiclePropValue> PropValueStore::readValueOrNull(
    const VehiclePropValue& request) const {
    MuxGuard g(mLock);
    RecordId recId = getRecordIdLocked(request);
    const VehiclePropValue* internalValue = getValueOrNullLocked(recId);
    return internalValue ? std::make_unique<VehiclePropValue>(*internalValue) : nullptr;
}

std::unique_ptr<VehiclePropValue> PropValueStore::readValueOrNull(int32_t prop, int32_t area) const {
    RecordId recId = {prop, isGlobalProp(prop) ? 0 : area};
    MuxGuard g(mLock);
    const VehiclePropValue* internalValue = getValueOrNullLocked(recId);
    return internalValue ? std::make_unique<VehiclePropValue>(*internalValue) : nullptr;
}

RecordId PropValueStore::getRecordIdLocked(const VehiclePropValue& valuePrototype) const {
    RecordId recId = {.prop = valuePrototype.prop,
                      .area = isGlobalProp(valuePrototype.prop) ? 0 : valuePrototype.areaId};
    return recId;
}

const VehiclePropValue* PropValueStore::getValueOrNullLocked(const RecordId& recId) const {
    auto it = mPropertyValues.find(recId);
    return it == mPropertyValues.end() ? nullptr : &it->second;
}

void PropValueStore::beginTransaction() {
    //MuxGuard g(mTransactionLock);
    std::unique_lock<std::mutex> lock(mTransactionLock);
    if (!mIsActive) {
        return;
    }
    while (mState == State::COMMITTING) {
        //正在提交中这里需要等待
        //std::unique_lock<std::mutex> lock(mCommitLock);
        mCommitCond.wait(lock);
    }
    if (mState == State::INIT) {
        mState.exchange(State::WAITE_COMMIT);
    }
    if (mState == State::WAITE_COMMIT) {
        mCommitCounter += 1;
    }
}

void PropValueStore::endTransaction() {
    MuxGuard g(mTransactionLock);
    if (!mIsActive) {
        return;
    }
    if (mState == State::WAITE_COMMIT) {
        mCommitCounter -= 1;
        if (mCommitCounter < 0) mCommitCounter = 0;
        if (mCommitCounter == 0) {
            if (mState.exchange(State::COMMITTING) == State::WAITE_COMMIT) {
                mReportCond.notify_one();
            }
        }
    }
}

void PropValueStore::deactivate() {
    {
        MuxGuard g(mTransactionLock);
        mIsActive = false;
        if (mState != State::COMMITTING) {
            mState.exchange(State::COMMITTING);
        }
    }
    mReportCond.notify_all();
}

void PropValueStore::waiteCommit() {
    std::unique_lock<std::mutex> lock(mTransactionLock);
    while (mState != State::COMMITTING) {
        mReportCond.wait(lock);
    }
}

void PropValueStore::commitCompleted() {
    MuxGuard g(mTransactionLock);
    if (!mIsActive) {
        return;
    }
    if (mState == State::COMMITTING) {
        if (mState.exchange(State::INIT) == State::COMMITTING) {
            mCommitCond.notify_one();
        }
    }
}

void PropValueStore::readReportPropValues(
    const std::function<void(const VehiclePropValue&)>& callback) {
    if (!mReportValues.empty()) {
        for (auto& value : mReportValues) {
            CollectValue* cv = &value.second;
            if (cv->mode == CommitMode::Direct || isPropertyChanged(cv->lastValue, cv->firstValue)) {
                callback(cv->lastValue);
            }
        }
        mReportValues.clear();
    }
}

}  // namespace ecarx::vhal::core::property::storage